/**
 * @author Da Mu
 * @version 0.1.0
 * @mail 317782199@qq.com
 */
import { Server, Socket } from 'socket.io';
import { DefaultEventsMap } from 'socket.io/dist/typed-events';
import { Injectable, Inject, forwardRef, NotFoundException } from '@nestjs/common';
import { Cron, SchedulerRegistry, CronExpression } from '@nestjs/schedule';
import { CronJob } from 'cron';
import { v4 as uuidv4 } from 'uuid';
import { TemplateApiRedisService } from '../TemplateApiPlugService/TemplateApiRedis.service';
import { TemplateApiChatGateway } from '../TemplateApiGateway/TemplateApiChat.gateway';
import { WINSTON_MODULE_PROVIDER } from 'nest-winston';
import { Logger as WinstonLogger } from 'winston';
import * as jsonfile from 'jsonfile';
import * as crypto from 'crypto';
import * as path from 'path';
import * as fs from 'fs';

import { BullMQService } from '../TemplateApiPlugService/TemplateApiBullmq.service';

import publishSubscrib from '../../utils/publishSubscrib';

import dayjs from 'dayjs';
import calculateTimeRemaining from '../../utils/calculateTimeRemaining';



class OnListenAddJobExecuteLock {
    static instance: any;
    static job(server: any) {
        publishSubscrib.subscribe('WORKER_JOB', (obj: any) => {
            /**@description all players info*/
            server.to('public_players_info').emit('IO_ROOM_PLAYERS_INFO_TIMER_QUEUE_DATA', obj);
        });
    };

    static main(server: any) {
        if (!OnListenAddJobExecuteLock.instance) {
            OnListenAddJobExecuteLock.instance = new OnListenAddJobExecuteLock();
            OnListenAddJobExecuteLock.job(server);
        };
        return OnListenAddJobExecuteLock.instance;
    };
};


@Injectable()
export class TemplateApiGameRoom {
    @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: WinstonLogger
    public client: Socket<DefaultEventsMap, DefaultEventsMap, DefaultEventsMap, any>;
    public server: Server;
    public playersOnlineNumber: number;
    public getUiStatusCronJob: any;
    public getUiAirStatusCronJob: any;
    public getUiBuildingStatusCronJob: any;
    private readonly filePath: string = path.join(process.cwd(), 'static/game', 'Timer.json');
    private readonly filePath_playerInfo: string = path.join(process.cwd(), 'static/game', 'PlayerInfo-dev.json');
    public airCronJobProgress: number;
    public airCronJobProgressStep: number;
    constructor(
        @Inject(forwardRef(() => TemplateApiChatGateway))
        private readonly chatGateway: TemplateApiChatGateway,
        private readonly redis: TemplateApiRedisService,
        public schedulerRegistry: SchedulerRegistry,
        private readonly mq: BullMQService
    ) {
        this.airCronJobProgress = 0;
        this.airCronJobProgressStep = 5;

        this.playersOnlineNumber = 0;

        this.getUiStatusCronJob = null;

        this.getUiAirStatusCronJob = null;

        this.getUiBuildingStatusCronJob = null;
    };

    async publicJoinGameWorld() {
        try {

            /**@description players online number */
            this.client.join('public_players_online_number');
            /**@description players chat message */
            // this.client.join('public_players_chat');
            /**@description players ready start game */
            this.client.join('public_players_ready_start_game');
            /**@description players start game */
            this.client.join('public_players_start_game');
            /**@description players info */
            this.client.join('public_players_info');
            /**@description airdrop cron job */
            this.client.join('public_airdrop_cron_job');
            /**@description building cron job */
            this.client.join('public_building_cron_job');
            /**@description enable3d scene */
            this.client.join('public_enable3d_scene');


            const playersOnlineNumber = await this.redis.get('players_online_number');
            this.server.to('public_players_online_number').emit('IO_ROOM_PLAYERS_ONLINE_NUMBER_DATA', playersOnlineNumber);

        } catch (error) {

        };
    };

    async publicPlaersReadyStartGame(body: any) {
        try {

            /**@description all players ready start game */
            this.server.to('public_players_ready_start_game').emit('IO_ROOM_PLAYERS_READY_START_GAME_DATA', body);

        } catch (error) {

        };
    };

    async publicPlaersStartGame(body: string) {
        try {

            /**@description all players start game */
            this.server.to('public_players_info').emit('IO_ROOM_PLAYERS_START_GAME_DATA', body);

        } catch (error) {

        };
    };

    async publicPlayersInfo(body: any) {
        try {

            const job = await this.mq.addJob(body.playerUID, body);

            OnListenAddJobExecuteLock.main(this.server);

        } catch (error) {

        };
    };

    async publicAirStatusCronJob(server: any) {
        if (this.schedulerRegistry.doesExist('cron', 'air_cron_job')) {
            this.getUiAirStatusCronJob = null;
            this.schedulerRegistry.deleteCronJob('air_cron_job');
        };

        this.getUiAirStatusCronJob = new CronJob(
            CronExpression.EVERY_5_SECONDS,
            async () => {
                if (this.airCronJobProgress >= 100) {
                    this.airCronJobProgress = 0;
                    return;
                };
                this.airCronJobProgress += this.airCronJobProgressStep;

                server.to('public_airdrop_cron_job').emit('IO_ROOM_UI_STATUS_AIR_CRON_JOB', this.airCronJobProgress);
                this.logger.info({ method: '@TemplateApiGame.room -> class.TemplateApiGameRoom.publicAirStatusCronJob', task: null });
            },
            null,
            false,
            'Asia/Shanghai'
        );

        this.schedulerRegistry.addCronJob('air_cron_job', this.getUiAirStatusCronJob);
        this.getUiAirStatusCronJob.start();
    };

    async publicBuildingStatusCronJob(server: any) {
        // const json = await jsonfile.readFileSync(this.filePath_playerInfo);

        try {

            if (this.schedulerRegistry.doesExist('cron', 'building_cron_job')) {
                this.getUiBuildingStatusCronJob = null;
                this.schedulerRegistry.deleteCronJob('building_cron_job');
            };

            this.getUiBuildingStatusCronJob = new CronJob(
                CronExpression.EVERY_30_SECONDS,
                async () => {

                    /**@description heartbeat detection */
                    const data = await this.redis.hgetall('all_online_players_uid');

                    for (let key in data) {
                        try {
                            const info = await this.redis.hgetall(key);
                            const building = JSON.parse(info.player_status_build);

                            if (building.playerBuildProgress) {
                                const endTime: any = JSON.parse(info.player_build_timer);
                                const time = calculateTimeRemaining.diff(endTime.remaining, dayjs().format('YYYY-MM-DD HH:mm:ss'));
                                if (time.totalMilliseconds === 0) {
                                    building.playerBuildProgress = false;
                                    building.playerBuild = 1;
                                    this.redis.hset(building.playerUID, 'player_status_build', JSON.stringify(building));
                                    server.to('public_building_cron_job').emit('IO_ROOM_UI_STATUS_BUILDING_CRON_JOB', {
                                        playerUID: building.playerUID,
                                        totalMilliseconds: 0,
                                        playerBuild: 1
                                    });
                                    this.logger.info({ method: '@TemplateApiGame.room -> class.TemplateApiGameRoom.publicBuildingStatusCronJob', task: null });
                                } else {
                                    server.to('public_building_cron_job').emit('IO_ROOM_UI_STATUS_BUILDING_CRON_JOB', {
                                        playerUID: building.playerUID,
                                        totalMilliseconds: time.totalMilliseconds,
                                        playerBuild: 0.2
                                    });
                                    this.logger.info({ method: '@TemplateApiGame.room -> class.TemplateApiGameRoom.publicBuildingStatusCronJob', task: null });
                                };
                            };
                        } catch (error) {
                            console.log(error);
                        };
                    };
                },
                null,
                false,
                'Asia/Shanghai'
            );

            this.schedulerRegistry.addCronJob('building_cron_job', this.getUiBuildingStatusCronJob);
            this.getUiBuildingStatusCronJob.start();

        } catch (error) {

        };

    };

    async publicStatusPlayerValue(body: string) {
        const playersStatus = await this.redis.hgetall(body);

        this.server.to('public_airdrop_cron_job').emit('IO_ROOM_UI_STATUS_PLAYER_VALUE_DATA', playersStatus);
    };

    async publicEnable3dSwitchScene(body: string) {

        this.server.to('public_enable3d_scene').emit('IO_ROOM_ENABLE3D_SWITCH_SCENE_DATA', body);
    };

};
